package com.ruoyi.common.utils;

import lombok.extern.log4j.Log4j2;
import org.springframework.web.bind.annotation.PostMapping;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import software.amazon.awssdk.services.s3.waiters.S3Waiter;
import software.amazon.awssdk.transfer.s3.FileDownload;
import software.amazon.awssdk.transfer.s3.FileUpload;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import sun.misc.BASE64Encoder;

import java.io.*;
import java.net.URL;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.CompletableFuture;

/**
 * @Author: ygw
 * @Date: 2022/04/11/14:15
 * @Description:
 */
@Log4j2
public class AwsUtil {

    /**
     * 创建桶
     *
     * @param bucketName
     * @return
     */
    public static String createBucket(String bucketName) {
        //创建客户端连接
        S3Client s3 = s3Client();
        //调用创建方法
        //String bucket = createBucket(s3, bucketName);
        // Build the ObjectOwnership for CreateBucket
        CreateBucketRequest createBucketRequest = CreateBucketRequest.builder()
                .bucket(bucketName)
                .acl("public-read")
                .build();

        // Send the request to S3
        s3.createBucket(createBucketRequest);

        s3.close();
        return "成功";
    }

    /**
     * 上传对象
     *
     * @param bucketName
     * @param key
     * @return
     */
    public static URL uploadObject(String bucketName, String key, String path) throws IOException {
        log.info("key:" + key);
        log.info("path:" + path);
        //创建 S3Client 客户端连接
        S3Client s3 = s3Client();
        //调用上传接口
        putS3Object(s3, bucketName, key, path);
        //调用生成URL接口
        URL url = getURL(s3, bucketName, key);
        s3.close();
        log.info("成功上传对象");
        return url;
    }


    /**
     * 下载对象
     *
     * @param bucketName
     * @param key
     * @return
     */
    @PostMapping("/getObject")
    public static String getObject(String bucketName, String key, String path) {
        long MB = 1024;
        Region region = Region.EU_CENTRAL_1;
        AwsBasicCredentials awsCreds = AwsBasicCredentials.create(
                "AKIASIQBHHQUQNIFLNF7",
                "jFXbHQweyKixbpx8cKQab0+N4Rc7IBiKGhgRBXlJ");

        S3TransferManager transferManager = S3TransferManager.builder()
                .s3ClientConfiguration(cfg -> cfg.region(region)
                        .credentialsProvider(StaticCredentialsProvider.create(awsCreds))
                        .targetThroughputInGbps(20.0)
                        .minimumPartSizeInBytes(1000 * MB))
                .build();

        downloadObjectTM(transferManager, bucketName, key, path);
        log.info("成功下载");
        transferManager.close();
        return "下载成功";
    }


    /**
     * 从s3中读取数据
     */
    public static String getObjectData(String bucketName, String key, String path) {
        //创建客户端连接
        S3Client s3 = s3Client();
        getObjectBytes(s3, bucketName, key, path);
        s3.close();
        return "读取成功";
    }

    /**
     * 获取位于 Amazon S3 存储桶中的对象的 URL
     */
    public static URL getObjectUrl(String bucketName, String key) {
        //创建客户端连接
        S3Client s3 = s3Client();
        URL url = getURL(s3, bucketName, key);
        s3.close();
        return url;
    }

    /**
     * 删除对象
     *
     * @param bucketName
     * @return
     */
    public static String deleteObject(String bucketName, String key) {
        //创建客户端连接
        S3Client s3 = s3Client();
        //删除对象
        DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
                .bucket(bucketName)
                .key(key)
                .build();

        s3.deleteObject(deleteObjectRequest);
        return "删除对象成功";
    }

    /**
     * 删除桶
     *
     * @param bucketName
     * @return
     */
    public static String deleteBucket(String bucketName) {
        //创建客户端连接
        S3Client s3 = s3Client();
        //删除
        DeleteBucketRequest deleteBucketRequest = DeleteBucketRequest.builder().bucket(bucketName).build();
        s3.deleteBucket(deleteBucketRequest);
        s3.close();
        return "删除桶成功";
    }

    /**
     * 列出桶
     *
     * @param
     * @return
     */
    public static List<String> listBuckets() {
        //创建客户端连接
        S3Client s3 = s3Client();
        //列出
        List<String> list = new ArrayList<>();
        ListBucketsRequest listBucketsRequest = ListBucketsRequest.builder().build();
        ListBucketsResponse listBucketsResponse = s3.listBuckets(listBucketsRequest);
        for (Bucket x : listBucketsResponse.buckets()) {
            log.info("name:" + x.name());
            String name = x.name();
            list.add(name);
        }
        return list;
    }

    /**
     * 列出桶对象
     *
     * @param
     * @return
     */
    public static List<String> listObjects(String bucketName) {
        //创建客户端连接
        S3Client s3 = s3Client();
        //列出
        List<String> list = new ArrayList<>();
        ListObjectsRequest listObjects = ListObjectsRequest
                .builder()
                .bucket(bucketName)
                .build();

        ListObjectsResponse res = s3.listObjects(listObjects);
        List<S3Object> objects = res.contents();
        for (ListIterator iterVals = objects.listIterator(); iterVals.hasNext(); ) {
            S3Object myValue = (S3Object) iterVals.next();
            log.info(" 密钥的名称为 " + myValue.key());
            list.add(myValue.key());
        }

        return list;
    }


    /**
     * 删除桶中多个对象
     */
    public static String deleteMultiObjects(String bucketName) {
        //创建客户端连接
        S3Client s3 = s3Client();
        deleteBucketObjects(s3, bucketName);
        s3.close();
        return "删除成功";
    }


    /**
     * 异步上传对象
     *
     * @param bucketName
     * @param key
     * @param path
     * @return
     * @throws IOException
     */
    public static URL asyncOps(String bucketName, String key, String path) throws IOException {
        //创建客户端连接
        AwsBasicCredentials awsCreds = AwsBasicCredentials.create(
                "AKIASIQBHHQUQNIFLNF7",
                "jFXbHQweyKixbpx8cKQab0+N4Rc7IBiKGhgRBXlJ");

        Region region = Region.EU_CENTRAL_1;
        S3AsyncClient s3 = S3AsyncClient.builder()
                .credentialsProvider(StaticCredentialsProvider.create(awsCreds))
                .region(region)
                .build();

        S3Client s3Client = S3Client.builder()
                .credentialsProvider(StaticCredentialsProvider.create(awsCreds))
                .region(region)
                .build();

        PutObjectRequest objectRequest = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(key)
                .build();

        // 将对象放入桶中
        CompletableFuture<PutObjectResponse> future = s3.putObject(objectRequest, AsyncRequestBody.fromFile(Paths.get(path)));
        //生成url
        URL url = getURL(s3Client, bucketName, key);
        future.whenComplete((resp, err) -> {
            try {
                if (resp != null) {
                    log.info("对象上传:" + resp);
                } else {
                    err.printStackTrace();
                }
            } finally {
                // 只有当您完全完成时才关闭客户端
                s3.close();
            }
        });
        future.join();
        log.info("上传成功: " + url);
        return url;
    }


    /**
     * 创建客户端连接
     *
     * @return
     */
    private static S3Client s3Client() {
        //创建客户端连接
        AwsBasicCredentials awsCreds = AwsBasicCredentials.create(
                "AKIASIQBHHQUQNIFLNF7",
                "jFXbHQweyKixbpx8cKQab0+N4Rc7IBiKGhgRBXlJ");

        Region region = Region.EU_CENTRAL_1;
        return S3Client.builder()
                .credentialsProvider(StaticCredentialsProvider.create(awsCreds))
                .region(region)
                .build();
    }

    /**
     * 创建桶
     *
     * @param s3Client
     * @param bucketName
     * @return
     */
    private static String createBucket(S3Client s3Client, String bucketName) {

        try {
            S3Waiter s3Waiter = s3Client.waiter();
            CreateBucketRequest bucketRequest = CreateBucketRequest.builder()
                    .bucket(bucketName)
                    .build();

            s3Client.createBucket(bucketRequest);
            HeadBucketRequest bucketRequestWait = HeadBucketRequest.builder()
                    .bucket(bucketName)
                    .build();

            WaiterResponse<HeadBucketResponse> waiterResponse = s3Waiter.waitUntilBucketExists(bucketRequestWait);
            waiterResponse.matched().response().ifPresent(System.out::println);
            log.info(bucketName + " is ready");

        } catch (S3Exception e) {
            log.error("Aws-createBucket error：{}", e.awsErrorDetails().errorMessage(), e);
        }
        return "创建成功";
    }


    private static void downloadObjectTM(S3TransferManager transferManager, String bucketName, String objectKey, String objectPath) {
        FileDownload download =
                transferManager.downloadFile(d -> d.getObjectRequest(g -> g.bucket(bucketName).key(objectKey))
                        .destination(Paths.get(objectPath)));
        download.completionFuture().join();
    }

    private static void uploadObjectTM(S3TransferManager transferManager, String bucketName, String objectKey, String objectPath) {
        FileUpload upload =
                transferManager.uploadFile(u -> u.source(Paths.get(objectPath))
                        .putObjectRequest(p -> {
                            p.bucket(bucketName);
                            p.key(objectKey);
                            p.acl("public-read");
                        }));
        upload.completionFuture().join();
    }

    private static void deleteBucketObjects(S3Client s3, String bucketName) {
        ArrayList<ObjectIdentifier> keys = new ArrayList<>();
        ObjectIdentifier objectId = null;

        objectId = ObjectIdentifier.builder()
                .key(bucketName)
                .build();
        keys.add(objectId);

        Delete del = Delete.builder()
                .objects(keys)
                .build();

        try {
            DeleteObjectsRequest multiObjectDeleteRequest = DeleteObjectsRequest.builder()
                    .bucket(bucketName)
                    .delete(del)
                    .build();

            s3.deleteObjects(multiObjectDeleteRequest);
            log.info("删除多个对象!");
        } catch (S3Exception e) {
            log.error("Aws-deleteBucketObjects error：{}", e.awsErrorDetails().errorMessage(), e);
        }
    }

    private static void getObjectBytes(S3Client s3, String bucketName, String keyName, String path) {
        try {
            GetObjectRequest objectRequest = GetObjectRequest
                    .builder()
                    .key(keyName)
                    .bucket(bucketName)
                    .build();

            ResponseBytes<GetObjectResponse> objectBytes = s3.getObjectAsBytes(objectRequest);
            byte[] data = objectBytes.asByteArray();

            // 将数据写入本地文件
            File myFile = new File(path);
            OutputStream os = new FileOutputStream(myFile);
            os.write(data);
            log.info("从S3对象成功获取字节数");
            os.close();

        } catch (IOException ex) {
            log.error("Aws-getObjectBytes IOException：", ex);
        } catch (S3Exception e) {
            log.error("Aws-getObjectBytes error：{}", e.awsErrorDetails().errorMessage(), e);
        }
    }

    private static URL getURL(S3Client s3, String bucketName, String keyName) {
        URL url = null;

        try {
            GetUrlRequest request = GetUrlRequest.builder()
                    .bucket(bucketName)
                    .key(keyName)
                    .build();

            url = s3.utilities().getUrl(request);
            log.info("The URL for  " + keyName + " is " + url.toString());
        } catch (S3Exception e) {
            log.error("Aws-getURL error：{}", e.awsErrorDetails().errorMessage(), e);
        }
        return url;
    }

    public static String putS3Object(S3Client s3,
                                     String bucketName,
                                     String objectKey,
                                     String objectPath) {
        try {
            /**
             * bucketName- 新对象将上传到的现有存储桶或访问点 ARN 的名称。
             * key- 存储新对象的键。
             * metadata- 对象元数据。指定了上传数据流的内容长度
             */
            PutObjectRequest putOb = PutObjectRequest.builder()
                    .bucket(bucketName)
                    .key(objectKey)
                    .acl("public-read")
                    .build();

            PutObjectResponse response = s3.putObject(putOb,
                    RequestBody.fromBytes(getObjectFile(objectPath)));
            return response.eTag();
        } catch (S3Exception e) {
            log.error("Aws-putS3Object error,", e);
        }
        return "";
    }

    private static byte[] getObjectFile(String filePath) {

        FileInputStream fileInputStream = null;
        byte[] bytesArray = null;

        try {
            File file = new File(filePath);
            bytesArray = new byte[(int) file.length()];
            fileInputStream = new FileInputStream(file);
            fileInputStream.read(bytesArray);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return bytesArray;
    }

    /**
     * s3文件预览
     *
     * @param path 文件存储路径
     */
    public static String DownloadFromS3(String bucketName, String path) {
        S3Client s3Client = s3Client();
        BASE64Encoder encoder = new BASE64Encoder();
        GetObjectRequest objectRequest = GetObjectRequest
                .builder()
                .key(path)
                .bucket(bucketName)
                .build();
        ResponseBytes<GetObjectResponse> objectBytes = s3Client.getObjectAsBytes(objectRequest);
        byte[] data = objectBytes.asByteArray();
        // 转换成base64文件流,用于前端数据解析
        return encoder.encodeBuffer(data).trim();
    }
}

